//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include "gdbstub.h"
#include <mantle.h>

Boolean g_bDbgStubEntry = FALSE;

#if (defined(_UDBAGENT) || defined(_KDBAGENT))

int_t g_nMemError = 0;
void (*volatile g_pMemFaultRoutine) (void) = NULL;

char g_outBuf[c_uBufSize + 32] = {'\0'};
char g_inBuf[c_uBufSize + 32] = {'\0'};

#if defined(_UDBAGENT)
extern DLinkNode g_udbBreakpointList;
extern DLinkNode g_udbSymInfoBlockList;
#endif //#if defined(_UDBAGENT)

#if defined(_KDBAGENT)
Breakpoint g_breakpoints[c_nNumberOfBreakpoints];
DLinkNode g_freeBreakpointList;
DLinkNode g_usedBreakpointList;
#endif //#if defined(_KDBAGENT)

bool_t g_bDetchedInLastContinueOrNextCmd = FALSE;
UInt32 g_uDetchedAddrInLastContinueOrNextCmd = 0;

bool_t g_bLastContinue = FALSE;

extern CProcess *g_pCurrentProcess;

StubContext *g_cpArmCurrentCxt = NULL;
InterruptContext *g_pCurrentCxt = NULL;

UInt32 GetRegister(int reg)
{
    return *((UInt32 *)g_cpArmCurrentCxt + reg );
}

EXTERN_C void SetMemError(void)
{
    g_nMemError = 1;
}

EXTERN_C void StubInnerInit()
{
#if defined(_UDBAGENT)
    g_udbBreakpointList.Initialize();
    g_udbSymInfoBlockList.Initialize();

#endif

#if defined(_KDBAGENT)
    g_freeBreakpointList.Initialize();
    g_usedBreakpointList.Initialize();

    for (ushort_t i = 0; i < c_nNumberOfBreakpoints; i++) {
        g_freeBreakpointList.InsertLast((DLinkNode *)&g_breakpoints[i]);
    }

#endif //#if defined(_KDBAGENT)
}

EXTERN_C bool_t HexCharToInt(const char ch, int_t *pn)
{
    if (ch >= '0' && ch <= '9') {
        *pn = ch  - '0';
        return TRUE;
    }
    if (ch >= 'a' && ch <= 'f') {
        *pn = ch - 'a' + 10;
        return TRUE;
    }
    if (ch >= 'A' && ch <= 'F') {
        *pn = ch - 'A' + 10;
        return TRUE;
    }

    return FALSE;
}

EXTERN_C bool_t HexStrToByte(const char *pszBuf, int_t *pn)
{
    int_t nHighNibble, nLowNibble;

    if (!HexCharToInt(*pszBuf, &nHighNibble) \
        || !HexCharToInt(*(pszBuf + 1), &nLowNibble)) {
        return FALSE;
    }

    *pn = (nHighNibble << 4) | nLowNibble;
    return TRUE;
}

EXTERN_C const char* HexWordStrToInt(const char *pszBuf, int_t *pVal)
{
    int_t i, nVal, nNibble;
    int_t nFoundZero, nLim;

    nFoundZero = 0;

    // found the '0' from the first char in buf until the char is
    // not '0', it is for a machine word.
    for (i = 0; i < 8; i++, pszBuf++) {
        if (*pszBuf != '0') break;

        nFoundZero = 1;
    }

    nLim = 8 - i;
    nVal = 0;

    //convert to the int.
    for (i = 0; i < nLim; i++, pszBuf++) {
        if (!HexCharToInt(*pszBuf, &nNibble)) {
            if (i == 0 && !nFoundZero) return NULL;

            *pVal = nVal;
            return pszBuf;
        }

        nVal = (nVal << 4) | nNibble;
    }

    if (HexCharToInt(*pszBuf, &nNibble)) return NULL;

    *pVal = nVal;

    return pszBuf;
}

EXTERN_C const char* ParseMemCommand(const char *pszBuf,
    byte_t **ppAddr, int_t *pCount)
{
    bool_t bReadOp;
    int_t nAddr, nCount;

    bReadOp = (*pszBuf == 'm');
    pszBuf++;

    pszBuf = HexWordStrToInt(pszBuf, &nAddr);
    if (pszBuf == NULL || *pszBuf != ',') return NULL;

    pszBuf++;

    pszBuf = HexWordStrToInt(pszBuf, &nCount);
    if (pszBuf == NULL || nCount <= 0) return NULL;

    if (bReadOp) {
        *ppAddr = (byte_t *)nAddr;
        *pCount = nCount;
        return pszBuf;
    }
    if (*pszBuf != ':') return NULL;

    pszBuf++;
    *ppAddr = (byte_t *)nAddr;
    *pCount = nCount;

    return pszBuf;
}


static Boolean CheckMemoryBlock(
        UInt32 base, UInt32 size, VSegment ** ppSegment)
{
    assert(size <= PAGE_SIZE);
    assert(base < base + size);

    DLinkNode *pCurrentList;
    VSegment *pSeg;
    
    if (ppSegment) *ppSegment= NULL;

    if (base < COMMON_LIMIT) {
        pCurrentList = &g_pCurrentProcess->m_AS.m_commList;
    }
    else {
        pCurrentList = &g_pCurrentProcess->m_AS.m_privList;
    }

    ForEachDLinkNode(VSegment *, pSeg, pCurrentList) {
        if (pSeg->m_uBase <= base
                && (base + size) <= (pSeg->m_uBase + pSeg->m_uSize)) {
            if (ppSegment) *ppSegment= pSeg;
            return TRUE;
        }
    }

    if ((PROXY_ENTRY_BASE <= base )
            && ((base + size) <= (PROXY_ENTRY_BASE + PAGE_SIZE))) {
        return TRUE;
    }

    if ((USER_BLOCK_BASE <= base)
            && (base + size) <= (USER_BLOCK_BASE + USER_BLOCK_SIZE)) {
        return TRUE;
    }

    return FALSE;
}

EXTERN_C Boolean CloneMemBlockForProcess(UInt32 uAddr, UInt32 uSize)
{
    VSegment *pSegment = NULL;
    if (!CheckMemoryBlock(uAddr, uSize, &pSegment)) {
        return FALSE;
    }

    ECode ec = pSegment->CloneMemBlockForProcess(uAddr, uSize);
    if(FAILED(ec)) {
        return FALSE;
    }

    return TRUE;
}


EXTERN_C bool_t HexStrToMem(const char *pszBuf, byte_t *pMem, int_t nCount)
{
    int_t i;
    int_t nByteVal;

    g_nMemError = 0;
    g_pMemFaultRoutine = SetMemError;

    if(IS_IN_USER_MODE(g_pCurrentCxt)) {    
        Boolean ret = CloneMemBlockForProcess((UInt32)pMem, nCount);
        if (!ret) {
            return FALSE;       
        }
    }
    
    for (i = 0; i < nCount; i++, pMem++, pszBuf += 2) {
        if (!HexStrToByte(pszBuf, &nByteVal)) {
            g_pMemFaultRoutine = NULL;
            return FALSE;
        }

        *pMem = (byte_t)nByteVal;
        if (g_nMemError) {
            g_pMemFaultRoutine = NULL;
            return FALSE;
        }
    }

    g_pMemFaultRoutine = NULL;

    return TRUE;
}

EXTERN_C const char *ParseRegCommand(const char *pszBuf, int_t* pReg,
    byte_t *pRegbytes, int_t *pCount)
{
    int_t nRetVal;
    uint_t uLen;

    pszBuf++;

    pszBuf = HexWordStrToInt(pszBuf, &nRetVal);
    if (pszBuf == NULL) return NULL;
    if (*pszBuf != '=') return NULL;

    pszBuf++;

    uLen = strlen(pszBuf);
    if (uLen == 0 || uLen > c_uRegBytes * 2 || (uLen & 0x1)) return NULL;

    if (!HexStrToMem(pszBuf, pRegbytes, uLen / 2)) return NULL;

    *pReg = nRetVal;
    *pCount = uLen / 2;

    return (pszBuf + uLen);
}

EXTERN_C char* MemToHexStr(const byte_t *pMem, char *pszBuf, int_t nCount)
{
    int_t i;
    uchar_t uch;

    for (i = 0; i < nCount; i++, pMem++) {
        uch = *(byte_t *)pMem;
        if (g_nMemError) {
            g_pMemFaultRoutine = NULL;
            return NULL;
        }

        *pszBuf++ = c_arrHexChars[uch >> 4];
        *pszBuf++ = c_arrHexChars[uch & 0x0f];
    }

    *pszBuf = '\0';
    g_pMemFaultRoutine = NULL;

    return pszBuf;
}

EXTERN_C bool_t ParseBreakpointCommand(const char *pszBuf, int_t *pType,
    byte_t **ppAddr, int_t *pLen)
{
    int_t nType, nAddr, nLen;

    pszBuf++;
    if (!HexCharToInt(*pszBuf, &nType) \
        || (*(pszBuf + 1) != ',')) {
        return FALSE;
    }

    pszBuf += 2;

    pszBuf = HexWordStrToInt(pszBuf, &nAddr);
    if(pszBuf == NULL || *pszBuf != ',') return FALSE;

    pszBuf++;
    pszBuf = HexWordStrToInt(pszBuf, &nLen);
    if (pszBuf == NULL || nLen < 1) return FALSE;

    *pType = nType;
    *ppAddr = (byte_t *)nAddr;
    *pLen  = nLen;

    return TRUE;
}

EXTERN_C void GetTargetCurrentState(const vector_t vector,
    StubContext *pStubCxt)
{
    int_t nReg, nSigVal;
    char *pszBuf;

    g_outBuf[0] = '\0';
    pszBuf = g_outBuf;

    nSigVal = ExceptionVectorToSignals((int_t)vector);

    *pszBuf++ = 'T';
    *pszBuf++ = c_arrHexChars[nSigVal >> 4];
    *pszBuf++ = c_arrHexChars[nSigVal % 16];

#ifdef _x86

    *pszBuf++ = c_arrHexChars[ESPINDEX];
    *pszBuf++ = ':';

    nReg = pStubCxt->esp;
    pszBuf = MemToHexStr((byte_t *)&nReg, pszBuf, c_uRegBytes);

    *pszBuf++ = ';';

    *pszBuf++ = c_arrHexChars[EBPINDEX];
    *pszBuf++ = ':';

    nReg = pStubCxt->ebp;
    pszBuf = MemToHexStr((byte_t *)&nReg, pszBuf, c_uRegBytes);

    *pszBuf++ = ';';

    *pszBuf++ = c_arrHexChars[EIPINDEX];
    *pszBuf++ = ':';

    nReg = pStubCxt->eip;
    pszBuf = MemToHexStr((byte_t *)&nReg, pszBuf, c_uRegBytes);

    *pszBuf++ = ';';

    *pszBuf = '\0';
#elif defined(_arm)

    *pszBuf++ = c_arrHexChars[R11INDEX];
    *pszBuf++ = ':';

    nReg = pStubCxt->r11;
    pszBuf = MemToHexStr((byte_t *)&nReg, pszBuf, c_uRegBytes);

    *pszBuf++ = ';';

    *pszBuf++ = c_arrHexChars[SPINDEX];
    *pszBuf++ = ':';

    nReg = pStubCxt->sp;
    pszBuf = MemToHexStr((byte_t *)&nReg, pszBuf, c_uRegBytes);

    *pszBuf++ = ';';

    *pszBuf++ = c_arrHexChars[PCINDEX];
    *pszBuf++ = ':';

//    nReg = pStubCxt->pc-4;
    /*
     * we already had corrected pc in HandleException().
     */
    nReg = pStubCxt->pc;

    pszBuf = MemToHexStr((byte_t *)&nReg, pszBuf, c_uRegBytes);

    *pszBuf++ = ';';

    *pszBuf++ = '1';
    *pszBuf++ = '9';
    *pszBuf++ = ':';

    nReg = pStubCxt->cpsr;
    pszBuf = MemToHexStr((byte_t *)&nReg, pszBuf, c_uRegBytes);

    *pszBuf++ = ';';

    *pszBuf = '\0';
#elif defined(_mips)

#else

#endif // _x86
}

EXTERN_C int_t ExceptionVectorToSignals(const int_t nExceptionVector)
{
    int_t nSigVal;

#ifdef _x86

    switch (nExceptionVector) {
        case 0:
            nSigVal = 8;
            break;   // divide by zero
        case 1:
            nSigVal = 5;
            break;   // debug exception
        case 3:
            nSigVal = 5;
            break;   // breakpoint
        case 4:
            nSigVal = 16;
            break;   // into instruction (overflow)
        case 5:
            nSigVal = 16;
            break;   // bound instruction
        case 6:
            nSigVal = 4;
            break;   // Invalid opcode
        case 7:
            nSigVal = 8;
            break;   // coprocessor not available
        case 8:
            nSigVal = 7;
            break;   // double fault
        case 9:
            nSigVal = 11;
            break;   // coprocessor segment overrun
        case 10:
            nSigVal = 11;
            break;   // Invalid TSS
        case 11:
            nSigVal = 11;
            break;   // Segment not present
        case 12:
            nSigVal = 11;
            break;   // stack exception
        case 13:
            nSigVal = 11;
            break;   // general protection
        case 14:
            nSigVal = 11;
            break;   // page fault
        case 16:
            nSigVal = 7;
            break;   // coprocessor error
        default:
            nSigVal = 7;  // "software generated"
    }
#elif defined(_arm)

    switch (nExceptionVector) {
        case 0:
            nSigVal = 5;
            break;   // "Undefined Instruction"
        default:
            nSigVal = 7;  // "Undefined ExceptionVector"
    }
#elif defined(_mips)

#else

#endif // _x86

    return (nSigVal);
}

EXTERN_C void DebugPrint(char *pidentify, char *pszInfo)
{
    kprintf("%s", pidentify);
    int len = strlen(pszInfo);
    if (len <= 512) {
        kprintf("%s", pszInfo);
    }
    else {
        char *p = pszInfo;
        char tm[513];
        do {
            memset(tm, 0, 513);
            strncpy(tm, p, 512);
            kprintf("%s", tm);
            p += 512;
        } while (strlen(p) > 512);
        if (*p) kprintf("%s", p);
    }
}

#endif //#if (defined(_UDBAGENT) || defined(_KDBAGENT))
